﻿/**
 * GoText Palm: A program that allows to send text messages over the internet.
 *  Copyright (C) 2008 Nicola 'Gawaine' Racco
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
using System;
using System.Linq;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Threading;
using GoTextPalm.Core;
using GoTextPalm.Core.Data;
using GoTextPalm.Core.Persistence;
using GoTextPalm.Core.Localization;
using System.Xml;
using System.IO;
using System.Net;

namespace GoTextPalm.Gui
{
    public partial class SendMessageForm : LocalizedForm
    {
        private Logic logic;

        private Thread task;
        private bool sent;
        private bool showErrors = true;

        public SendMessageForm(Service Service, Message m)
        {
            logic = new Logic();
            logic.ErrorCausedEvent += new Logic.ErrorCausedHandler(Logic_ErrorCaused);
            logic.ImageCodeRequestedEvent += new Logic.ImageCodeRequestedHandler(Logic_ImageCodeRequested);
            logic.ProgressBarToNextEvent += new Logic.ProgressBarToNextHandler(Logic_ProgressBarToNext);
            logic.SendProcedureEndedEvent += new Logic.SendProcedureEndedHandler(Logic_SendProcedureEnded);
            logic.StatusTextChangedEvent += new Logic.StatusTextChangedHandler(Logic_StatusTextChanged);
            logic.Service = Service;
            logic.Message = m;

            InitializeComponent();
            header.Text = "Sending Message";
            AddMenusToDictionary();
            UpdateResources();
        }

        protected override void AddMenusToDictionary()
        {
            AddMenuToDictionary(cancelMnu, "cancelMnu");
        }

        private void cancelMnu_Click(object sender, EventArgs e)
        {
            try
            {
                task.Abort();
            }
            catch { }
            sent = false;
            showErrors = false;
            Close();
        }

        private void SendMessage_Load(object sender, EventArgs e)
        {
            pbar.Maximum = 4;
            task = new Thread(new ThreadStart(logic.SendProcedure));
            task.Start();
        }

        private void btnContinue_Click(object sender, EventArgs e)
        {
            if (txtCod.TextLength == 0)
                MessageBox.Show(Utility.Tr("SendMessageForm_needic",
                    "Insert the Image Code to continue"), header.Text,
                    MessageBoxButtons.OK, MessageBoxIcon.Exclamation,
                    MessageBoxDefaultButton.Button1);
            else
            {
                logic.ImageCode = txtCod.Text;
                pnlImage.Visible = false;
                pbar.Maximum = 7;
                task = new Thread(new ThreadStart(logic.SendProcedure));
                task.Start();
            }
        }
        
        private void SendMessageForm_Closing(object sender, CancelEventArgs e)
        {
            DialogResult = sent ? DialogResult.OK : DialogResult.Cancel;
        }

        public void Logic_StatusTextChanged(String text)
        {
            if (InvokeRequired)
            {
                Logic.StatusTextChangedHandler func = Logic_StatusTextChanged;
                Invoke(func, text);
                return;
            }
            if (!animator.Started)
                animator.Started = true;
            lblStatus.Text = text;
        }

        public void Logic_ProgressBarToNext()
        {
            if (InvokeRequired)
            {
                Logic.ProgressBarToNextHandler func = Logic_ProgressBarToNext;
                Invoke(func);
                return;
            }
            pbar.Value++;
        }

        public void Logic_ErrorCaused(String text)
        {
            if (InvokeRequired)
            {
                Logic.ErrorCausedHandler func = Logic_ErrorCaused;
                Invoke(func, text);
                return;
            }
            animator.Started = false;
            if (showErrors)
                MessageBox.Show(text, header.Text, MessageBoxButtons.OK, 
                    MessageBoxIcon.Exclamation, MessageBoxDefaultButton.Button1);
            Close();
        }

        public void Logic_SendProcedureEnded(ServerResponse sr)
        {
            if (InvokeRequired)
            {
                Logic.SendProcedureEndedHandler func = Logic_SendProcedureEnded;
                Invoke(func, sr);
                return;
            }

            if (sr.Type == ServerResponse.ResponseType.DONE)
            {
                Service Service = logic.Service;
                Service.Stat.Date = DateTime.Now;
                Service.Stat.Sent = (sr.Remains != -1 ? Service.MaxMessages - sr.Remains : Service.Stat.Sent + 1);
                DBService.UpdateServiceStat(Service);
                animator.Started = false;
                MessageBox.Show(Utility.Tr("SendMessageForm_mexsent", "Message sent!"), 
                    header.Text, MessageBoxButtons.OK, MessageBoxIcon.None, 
                    MessageBoxDefaultButton.Button1);
                sent = true;
                int i = 0;
                if (ServiceLogic.GetSavingPolicy() == 1
                    && (i = ServiceLogic.GetSavingMessagesEnabled()) >= 0)
                {
                    bool save = true;
                    if (i == 1)
                    {
                        DialogResult dr = MessageBox.Show(Utility.Tr("NewMessageForm_savemsg",
                        "Do you want to save this message ?"), header.Text,
                        MessageBoxButtons.YesNoCancel, MessageBoxIcon.Exclamation,
                        MessageBoxDefaultButton.Button1);
                        if (dr == DialogResult.Cancel)
                            return;
                        else if (dr == DialogResult.No)
                            save = false;
                    }
                    if (save)
                        ServiceLogic.SaveMessage(Service.Id.Value, ref logic.Message);
                }
                if (ServiceLogic.GetSavingLastMessageEnabled())
                    ServiceLogic.SaveLastMessage(logic.Service, ref logic.Message);
                Close();
            }
            else
            {
                animator.Started = false;
                String error = Utility.Tr("SendMessageForm_errorcnt", "Server: {0}\rgTP: {1}");
                String srvErr = sr.Message.Trim();
                switch (sr.Type)
                {
                    case ServerResponse.ResponseType.DATA_ERROR:
                        error = String.Format(error, srvErr, Utility.Tr("SendMessageForm_err1", "Maybe recipients are malformed or there are errors in the message"));
                        break;
                    case ServerResponse.ResponseType.LOGIN_ERROR:
                        error = String.Format(error, srvErr, Utility.Tr("SendMessageForm_err2", "Maybe login failed"));
                        break;
                }
                MessageBox.Show(error, header.Text, MessageBoxButtons.OK, MessageBoxIcon.None, MessageBoxDefaultButton.Button1);
                Close();
            }
        }

        public void Logic_ImageCodeRequested(Bitmap code)
        {
            if (InvokeRequired)
            {
                Logic.ImageCodeRequestedHandler func = Logic_ImageCodeRequested;
                Invoke(func, code);
                return;
            }

            animator.Started = false;
            lblStatus.Text = Utility.Tr("SendMessageForm_waitic", "Waiting Image Code ...");
            icPicture.Size = code.Size;
            icPicture.Image = code;
            pnlImage.Visible = true;
        }


        private class Logic
        {
            public delegate void StatusTextChangedHandler(String text);
            public event StatusTextChangedHandler StatusTextChangedEvent;

            public delegate void ProgressBarToNextHandler();
            public event ProgressBarToNextHandler ProgressBarToNextEvent;

            public delegate void ImageCodeRequestedHandler(Bitmap image);
            public event ImageCodeRequestedHandler ImageCodeRequestedEvent;

            public delegate void SendProcedureEndedHandler(ServerResponse sr);
            public event SendProcedureEndedHandler SendProcedureEndedEvent;

            public delegate void ErrorCausedHandler(String text);
            public event ErrorCausedHandler ErrorCausedEvent;

            internal Service Service;
            private String Cookies = null;
            internal String ImageCode = null;
            internal Message Message;

            private static String FormatText(String text)
            {
                StringBuilder sb = new StringBuilder(text);
                sb.Replace("%", "%25");
                sb.Replace("+", "%2B");
                sb.Replace("&", "%26");
                sb.Replace("$", "%24");
                sb.Replace("\\", "%2F");
                sb.Replace("/", "%2F");
                sb.Replace("=", "%3D");
                sb.Replace("'", "%27");
                sb.Replace(";", "%3B");
                sb.Replace("Ãˆ", "%C9");
                sb.Replace("Ã‰", "%C9");
                sb.Replace("Ã ", "%E0");
                sb.Replace("Ã¡", "%E1");
                sb.Replace("Ã¨", "%E8");
                sb.Replace("Ã©", "%E9");
                sb.Replace("Ã¬", "%EC");
                sb.Replace("Ã­", "%ED");
                sb.Replace("Ã²", "%F2");
                sb.Replace("Ã³", "%F2");
                sb.Replace("Ã¹", "%F9");
                sb.Replace("#", "%23");
                sb.Replace("Â£", "%A3");
                sb.Replace("@", "%40");
                sb.Replace(" ", "%20");
                return sb.ToString();
            }

            public void SendProcedure()
            {
                try
                {
                    FireStatusTextChanged(Utility.Tr("ServiceLogic_sts1", "Request Creation ..."));
                    StringBuilder sb = new StringBuilder(Constants.PARAM_SPV);
                    if (ImageCode == null)
                    {
                        if (Service.NeedsUsername)
                            sb.Append("&user=" + FormatText(Service.Config.Username.Value));
                        if (Service.NeedsPassword)
                            sb.Append("&pass=" + FormatText(Service.Config.Password.Value));
                        if (Service.NeedsNickname)
                        {
                            sb.Append("&nick");
                            if (Service.Config.Nickname.Value.Length != 0)
                                sb.Append("=" + FormatText(Service.Config.Nickname.Value));
                        }
                        sb.Append("&text=" + FormatText(Message.Text));
                        StringBuilder rcpt = null;
                        foreach (MessageRecipient mr in Message.Recipients)
                            if (rcpt == null)
                                rcpt = new StringBuilder(mr.Value);
                            else
                                rcpt.Append(";" + mr.Value);
                        sb.Append("&rcpt=" + FormatText(rcpt.ToString()));
                    }
                    else
                        sb.Append("&ic=" + FormatText(ImageCode));
                    sb.Append("&lang=" + FormatText(DBConfig.get("lang").Substring(0, 2)));
                    byte[] data = new ASCIIEncoding().GetBytes(sb.ToString());

                    FireProgressBarToNext();
                    FireStatusTextChanged(Utility.Tr("ServiceLogic_sts2", "Connection to server ..."));
                    HttpWebRequest request = (HttpWebRequest)WebRequest.Create(Service.Url);
                    if (ImageCode != null && Cookies != null)
                        request.Headers["Cookie"] = Cookies;

                    FireProgressBarToNext();
                    FireStatusTextChanged(Utility.Tr("ServiceLogic_sts3", "Sending request ..."));
                    request.Method = "POST";
                    request.ContentType = "application/x-www-form-urlencoded";
                    request.ContentLength = data.Length;
                    Stream stream = request.GetRequestStream();
                    stream.Write(data, 0, data.Length);
                    stream.Close();

                    FireProgressBarToNext();
                    FireStatusTextChanged(Utility.Tr("ServiceLogic_sts4", "Fetching Response ..."));
                    HttpWebResponse response = (HttpWebResponse)request.GetResponse();
                    if (response.ContentType.StartsWith("image"))
                    {
                        Stream reader = response.GetResponseStream();
                        Bitmap bm = new Bitmap(reader);
                        reader.Close();
                        Cookies = response.Headers["Cookie"];
                        response.Close();
                        FireImageCodeRequested(bm);
                    }
                    else
                    {
                        XmlReader reader = XmlReader.Create(response.GetResponseStream());
                        XmlDocument doc = new XmlDocument();
                        XmlNode resNode = null;
                        while ((resNode = doc.ReadNode(reader)) != null && resNode.Name != "res")
                            resNode = doc.ReadNode(reader);

                        ServerResponse.ResponseType type = ServerResponse.ResponseType.OTHER_ERROR;
                        String responseText = "";
                        int srm = -1;
                        foreach (XmlNode node in resNode.ChildNodes)
                            if (node.Name == "num")
                                type = (ServerResponse.ResponseType)Convert.ToInt32(node.InnerText);
                            else if (node.Name == "txt")
                                responseText = node.InnerText;
                            else if (node.Name == "srm" || node.Name == "srmm")
                                try
                                {
                                    srm = Convert.ToInt32(node.InnerText);
                                }
                                catch { }
                        reader.Close();
                        response.Close();

                        if (type == ServerResponse.ResponseType.OTHER_ERROR && responseText.Length == 0)
                            FireErrorCaused(Utility.Tr("ServiceLogic_badsrvres", "The Service response is incorrect"));
                        FireSendProcedureEnded(new ServerResponse(type, responseText, srm));
                    }
                }
                catch (XmlException e)
                {
                    FireErrorCaused(Utility.Tr("ServiceLogic_badsrvres", "The Service response is incorrect"));
                }
                catch (Exception e)
                {
                    FireErrorCaused(Utility.Tr("ServiceLogic_loadex1", "Cannot connect to the service. Maybe the internet connection is absent"));
                }
            }

            private void FireStatusTextChanged(String text)
            {
                if (StatusTextChangedEvent != null)
                    StatusTextChangedEvent(text);
            }

            private void FireProgressBarToNext()
            {
                if (ProgressBarToNextEvent != null)
                    ProgressBarToNextEvent();
            }

            private void FireErrorCaused(String text)
            {
                if (ErrorCausedEvent != null)
                    ErrorCausedEvent(text);
            }

            private void FireSendProcedureEnded(ServerResponse sr)
            {
                if (SendProcedureEndedEvent != null)
                    SendProcedureEndedEvent(sr);
            }

            private void FireImageCodeRequested(Bitmap icon)
            {
                if (ImageCodeRequestedEvent != null)
                    ImageCodeRequestedEvent(icon);
            }
        }
    }
}